有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用SwingGUI的java简单ClientServer程序

我正在制作一个简单的,无线程客户端-服务器程序,其中GUI在服务器客户端上都有一个按钮。当客户端按下按钮时,它将按钮上的文本更改为“C”,并发送到服务器“C”字符串,因此服务器侧的按钮将文本更改为“C”服务器的工作原理与客户端类似,但发送的“S”而不是的“C”。它们轮流工作:客户端的轮到时,服务器的按钮被锁定,他无法更改按钮客户端始终首先启动

客户端按下按钮时,它工作正常,但当服务器按下按钮时,它会在服务器侧将按钮更改为“S”,而不是在客户端侧。我知道我做错了什么

服务器代码:

public class Serv implements ActionListener
{
    private JButton button;
    private boolean myTurn;
    private ServerSocket sock;
    private Socket s;
    private BufferedReader input;
    private PrintStream output;

    public Serv() throws UnknownHostException, IOException
    {
        button = new JButton();
        myTurn = false;
        sock = new ServerSocket(9001);
        s = null;
        button = new JButton();
    }

    public void createGUI()
    {
        JFrame frame = new JFrame("TicTacToe - Server");
        JPanel mainPanel = new JPanel();
        mainPanel.setPreferredSize(new Dimension(100, 100));
        button = new JButton("");
        button.setPreferredSize(new Dimension(100, 100));
        button.setFont(new Font(button.getFont().getName(), button.getFont().getStyle(), 70));
        button.setActionCommand("1");
        button.addActionListener(this);
        mainPanel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void startMyGame() throws IOException
    {
        createGUI();
        s = sock.accept();
        input = new BufferedReader(new InputStreamReader(s.getInputStream()));
        output = new PrintStream(s.getOutputStream(), true);
        while(true)
        {
            if(myTurn == false)
            {
                myTurn = true;
                String out = input.readLine();
                button.setText(out);
            }
        }
    }

    public static void main(String args[]) 
    {
        Serv tc = null;
        try
        {
            tc = new Serv();
            tc.startMyGame();

        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                tc.close();
            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
        }

    }

    private void close() throws IOException
    {
        this.sock.close();
        this.input.close();
        this.output.close();
    }

    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(myTurn == true)
        {
            if(e.getActionCommand().equals("1"))
            {
                JButton b = (JButton) e.getSource();
                b.setText("S");
                output.println("S");
                myTurn = false;
            }
        }
    }
}

客户端代码:

public class Cli implements ActionListener
{
    private JButton button;
    private boolean myTurn;
    private Socket sock;
    private BufferedReader input;
    private PrintStream output;

    public Cli() throws UnknownHostException, IOException
    {
        button = new JButton();
        myTurn = true;
        sock = new Socket("127.0.0.1", 9001);
        input = new BufferedReader(new InputStreamReader(sock.getInputStream()));
        output = new PrintStream(sock.getOutputStream(), true);
    }

    public void createGUI()
    {
        JFrame frame = new JFrame("TicTacToe - Client");
        JPanel mainPanel = new JPanel();
        mainPanel.setPreferredSize(new Dimension(100, 100));
        button = new JButton("");
        button.setPreferredSize(new Dimension(100, 100));
        button.setFont(new Font(button.getFont().getName(), button.getFont().getStyle(), 70));
        button.setActionCommand("1");
        button.addActionListener(this);
        mainPanel.add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public void startMyGame() throws IOException
    {
        createGUI();
        while(true)
        {
            if(myTurn == false)
            {
                myTurn = true;
                String out = input.readLine();
                button.setText(out);
            }
        }
    }

    private void close() throws IOException
    {
        this.sock.close();
        this.input.close();
        this.output.close();
    }

    public static void main(String args[]) 
    {
        Cli tc = null;
        try
        {
            tc = new Cli();
            tc.startMyGame();

        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            try
            {
                tc.close();
            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(myTurn == true)
        {
            if(e.getActionCommand().equals("1"))
            {
                JButton b = (JButton) e.getSource();
                if(!b.getText().equals("X") || !b.getText().equals("O"))
                {
                    b.setText("C");
                    output.println("C");
                    myTurn = false;
                }
            }
        }
    }
}

我已经删除了导入,因此代码会更短


共 (1) 个答案

  1. # 1 楼答案

    代码当前存在的问题:

    • 您正在创建一个Swing GUI,并在Swing事件调度线程或EDT的下运行它。GUI应该在事件线程上启动,这样所有Swing代码都可以保证在单个线程上运行
    • 您有一个长时间运行的while循环,它正在进行Swing变异调用,更新JButton的状态。如果此代码在Swing事件线程上运行,它将阻止/冻结GUI。这个块应该在不是EDT的后台线程中显式调用,所有Swing调用都应该按照Lesson: Concurrency in Swing教程排队到事件线程中
    • 您在不同的线程中使用了非易失性布尔值,这可能会导致变量在应该更改时无法更改
    • 您似乎立即关闭了您的流,从而妨碍了关注点之间的充分通信

    正在制作一个更干净的示例

    例如:

    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.io.IOException;
    import java.io.PrintStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    
    import javax.swing.*;
    
    public class SimpleServerClient {
        private static final int PORT = 9001;
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> {
                try {
                    SimpleServer server = new SimpleServer(PORT, "Server", false);
                    SimpleClient client = new SimpleClient(PORT, "Client", true);
                    server.createGui();
                    client.createGui();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
    

    interface SimpleGui {
        void sendLine(String nextLine);
    }
    

    // background thread handles listening to the Scanner 
    // which scans a Socket's InputStream
    class MyWorker extends SwingWorker<Void, Void> {
        public static final String LINE = "line";
        private Scanner inputScanner;
        private SimpleGui gui;
        private String line = "";
    
        public MyWorker(Scanner inputScanner, SimpleGui gui) {
            this.inputScanner = inputScanner;
            this.gui = gui;
        }
    
        @Override
        protected Void doInBackground() throws Exception {
            while (inputScanner.hasNext()) {
                // get line from Scanner                
                // use the setter method in case we want to use a PropertyChangeListener later
                setLine(inputScanner.nextLine());
    
                // send line to the GUI
                gui.sendLine(getLine());
            }
            return null;
        }
    
        public String getLine() {
            return line;
        }
    
        // again rigged up to allow use of PropertyChangeListeners
        public void setLine(String line) {
            this.line = line;
            firePropertyChange(LINE, null, line);
        }
    }
    

    // code that both the client and server GUI classes share
    abstract class DefaultGui implements SimpleGui {
    
        // this guy ***must**** be volitile!
        private volatile boolean myTurn;
        protected Scanner inputScanner;
        protected PrintStream out;
        protected JButton button = new JButton("Blank");
        protected Socket socket;
        protected String name;
        protected int port;
    
        public DefaultGui(int port, String name, boolean myTurn) {
            this.port = port;
            this.name = name;
            this.myTurn = myTurn;
        }
    
        @Override
        public void sendLine(String nextLine) {
            button.setText(nextLine);
            myTurn = true;
        }
    
        public void createGui() {
            button.addActionListener(e -> actionPerformed(e));
            JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(300, 300));
            panel.add(button);
    
            JFrame frame = new JFrame(getName());
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(panel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
    
        protected void actionPerformed(ActionEvent e) {
            if (!myTurn) {
                return;
            }
            out.println(getName());
            button.setText(getName());
            myTurn = false;
        }
    
        public String getName() {
            return name;
        }
    
    }
    

    class SimpleServer extends DefaultGui {
        private ServerSocket serverSocket;
    
        public SimpleServer(int port, String name, boolean myTurn) throws IOException {
            super(port, name, myTurn);
            serverSocket = new ServerSocket(port);
            new Thread(() -> {
                try {
                    // accept() blocks the current thread, so must be called on a background thread
                    socket = serverSocket.accept();
                    inputScanner = new Scanner(socket.getInputStream());
                    out = new PrintStream(socket.getOutputStream(), true);
                    new MyWorker(inputScanner, this).execute();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }    
    }
    

    class SimpleClient extends DefaultGui {
    
        public SimpleClient(int port, String name, boolean myTurn) throws IOException {
            super(port, name, myTurn);
            socket = new Socket("localhost", port);
            inputScanner = new Scanner(socket.getInputStream());
            out = new PrintStream(socket.getOutputStream());
            new MyWorker(inputScanner, this).execute();
        }    
    }